############################################################################
# Copyright (C) 2012 Guangzhou Liubao ComputerTech Co.,Ltd. <www.liubao.com>
# Copyright (c) 2012    Huzhou Xunpu InfoTech Co.,Ltd.     <www.pyforce.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License,
# or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
############################################################################

__version__, revision = '1.0', '$Revision: 445 $'
if revision.find(':') >= 0:
    revision = revision[revision.find(':')+1:revision.rfind('$')].strip()
    __version__ = '%s:%s' % (__version__, revision)
else:
    revision = ''

class context:
    def __init__(self, http, mapfso, sess, conn=None):
        self.__get_mapfso = ref(mapfso)
        self.__get_sess = ref(sess)
        if conn is None:
            self.environ = http.environ
            self.__get_conn = None
            self.__buf = deque()
            self.__bufsize = 0
            self.__http = http
        else:
            self.__get_conn = ref(conn)
            self.data = conn.data

    def userid(self):
        sess = self.__get_sess()
        if sess is None:
            return None
        return sess.userid

    username = userid = property(userid)

    def user(self):
        sess = self.__get_sess()
        if sess is None:
            return None
        mapfso = self.__get_mapfso()
        user = mapfso.users.get(sess.userid)
        if user is None:
            return None
        return userwrap(user)

    user = property(user)

    def session(self):
        sess = self.__get_sess()
        if sess is None:
            return None
        return sesswrap(sess)

    session = property(session)

    def connection(self):
        connref = self.__get_conn
        if connref is None:
            raise AttributeError(
                'HTMLContext object has no attribute \'connection\'')
        conn = connref()
        if conn is None:
            return None
        return connwrap(conn)

    connection = property(connection)

    def connections(self):
        sess = self.__get_sess()
        if sess is None:
            return []
        userid = sess.userid
        if userid is None:
            result = [connwrap(conn1.value
                ) for connid1, conn1 in sess.connections.data.items()]
            return result
        mapfso = self.__get_mapfso()
        user = mapfso.users.get(sess.userid)
        if user is None:
            return []
        result = []
        for sessid1, sess1 in user.sessions.data.items():
            for connid1, conn1 in sess1.connections.data.items():
                result.append(connwrap(conn1.value))
        return result

    connections = property(connections)

    def get_form(self, max_size=0x100000, timeout=-1):
        if self._form is None:
            self._form = form1 = form(self.__http, max_size, timeout)
            return form1
        form1 = self._form
        assert isinstance(form1, form)
        return form1

    form = property(get_form)

    def get_upload(self, max_size=0x100000, timeout=-1):
        if self._form is None:
            self._form = form1 = multipart(self.__http, max_size, timeout)
            return form1
        form1 = self._form
        assert isinstance(form1, multipart)
        return form1

    upload = property(get_upload)

    def login(self, userid):
        sess = self.__get_sess()
        assert sess is not None
        sess.login(userid)

    def logout(self):
        sess = self.__get_sess()
        if sess is None:
            return
        sess.logout()

    def findmodule(self, path):
        mapfso = self.__get_mapfso()
        module = mapfso.findmodule(path)
        return module

    def submit(self, path, args, timeout=60.):
        connref = self.__get_conn
        if connref is None:
            raise AttributeError(
                'HTMLContext object has no attribute \'submit\'')
        conn = connref()
        if conn is None:
            raise error(EPIPE, 'Broken pipe')
        return conn.submit(path, args, timeout)

    def secure_submit(self, path, args, timeout=60.):
        connref = self.__get_conn
        if connref is None:
            raise AttributeError(
                'HTMLContext object has no attribute \'secure_submit\'')
        conn = connref()
        if conn is None:
            raise error(EPIPE, 'Broken pipe')
        return conn.secure_submit(path, args, timeout)

    def exec_(self, code):
        connref = self.__get_conn
        if connref is None:
            raise AttributeError(
                'HTMLContext object has no attribute \'exec_\'')
        conn = connref()
        if conn is None:
            raise error(EPIPE, 'Broken pipe')
        return conn.exec_(code)

    def secure_exec(self, code):
        connref = self.__get_conn
        if connref is None:
            raise AttributeError(
                'HTMLContext object has no attribute \'secure_exec\'')
        conn = connref()
        if conn is None:
            raise error(EPIPE, 'Broken pipe')
        return conn.secure_exec(code)

    def read(self, size=-1):
        if self.__get_conn is not None:
            raise AttributeError(
                'JSRPCContext object has no attribute \'read\'')
        assert self._form is None
        return self.__http.read(size)

    def readline(self, size=-1):
        if self.__get_conn is not None:
            raise AttributeError(
                'JSRPCContext object has no attribute \'readline\'')
        assert self._form is None
        return self.__http.readline(size)

    def start_response(self, status='200 OK', response_headers=None):
        if self.__get_conn is not None:
            raise AttributeError(
                'JSRPCContext object has no attribute \'start_response\'')
        http = self.__http
        s = http.socket
        buf = ['%s %s\r\nTransfer-Encoding: chunked' % (
            http.environ['SERVER_PROTOCOL'], status)]
        if self.background_response_headers is not None:
            background_response_headers = self.background_response_headers
            if isinstance(background_response_headers, basestring):
                buf.append(background_response_headers)
            else:
                for pair in background_response_headers:
                    if isinstance(pair, basestring):
                        buf.append(pair)
                    else:
                        buf.append('%s: %s' % (pair[0], pair[1]))
        if response_headers is not None:
            if isinstance(response_headers, basestring):
                buf.append(response_headers)
            else:
                for pair in response_headers:
                    if isinstance(pair, basestring):
                        buf.append(pair)
                    else:
                        buf.append('%s: %s' % (pair[0], pair[1]))
        buf.append('')
        data = '\r\n'.join(buf)
        self.__http.socket.write(data, 16. + (len(data) >> 10))

    def write(self, data):
        if self.__get_conn is not None:
            raise AttributeError(
                'JSRPCContext object has no attribute \'write\'')
        self.__buf.append(data)
        self.__bufsize = bufsize = self.__bufsize + len(data)
        if bufsize > 8192:
            self.flush()

    def writelines(self, *args):
        if self.__get_conn is not None:
            raise AttributeError(
                'JSRPCContext object has no attribute \'writelines\'')
        bufsize = self.__bufsize
        buf = self.__buf
        if 1 == len(args):
            args = args[0]
            if isinstance(args, basestring):
                self.__buf.append(args)
                self.__bufsize = bufsize = self.__bufsize + len(args)
                if bufsize > 8192:
                    self.flush()
                return
        for data in args:
            buf.append(data)
            bufsize += len(data)
        self.__bufsize = bufsize
        if bufsize > 8192:
            self.flush()

    def flush(self):
        if self.__get_conn is not None:
            raise AttributeError(
                'JSRPCContext object has no attribute \'flush\'')
        s = self.__http.socket
        buf = deque()
        bufsize = 0
        for data in self.__buf:
            size = len(data)
            if size > 6144:
                if 0 == bufsize:
                    s.write('\r\n%x\r\n' % size, 32.)
                    s.write(data, 16. + (size >> 10))
                else:
                    buf.appendleft('\r\n%x\r\n' % bufsize)
                    buf.append('\r\n%x\r\n' % size)
                    s.write(''.join(buf), 32.)
                    s.write(data, 16. + (size >> 10))
                buf = deque()
                bufsize = 0
            elif 0 == size:
                continue
            else:
                buf.append(data)
                bufsize += size
                if bufsize > 6144:
                    buf.appendleft('\r\n%x\r\n' % bufsize)
                    s.write(''.join(buf), 32.)
                    buf = deque()
                    bufsize = 0
        self.__buf = buf
        self.__bufsize = bufsize

    def close(self):
        if self.__get_conn is not None:
            raise AttributeError(
                'JSRPCContext object has no attribute \'close\'')
        size = self.__bufsize
        http = self.__http
        if size > 0:
            if size > 6144:
                self.flush()
                size = self.__bufsize
                if size == 0:
                    del self.__buf, self.__bufsize
                    http.socket.write('\r\n0\r\n\r\n', 16.)
                    return http.close()
            buf = self.__buf
            del self.__buf, self.__bufsize
            buf.appendleft('\r\n%x\r\n' % size)
            buf.append('\r\n0\r\n\r\n')
            http.socket.write(''.join(buf), 32.)
            return http.close()
        del self.__buf, self.__bufsize
        http.socket.write('\r\n0\r\n\r\n', 16.)
        http.close()

    _form = background_response_headers = None

from UserDict import UserDict

class userwrap(UserDict):
    def __init__(self, user):
        self.__get_user = ref(user)
        self.data = user.data

    def userid(self):
        user = self.__get_user()
        if user is None:
            raise RuntimeError('this user does not exists')
        return user.userid

    id_ = username = userid = property(userid)

    def connections(self):
        user = self.__get_user()
        if user is None:
            raise RuntimeError('this user does not exists')
        result = []
        for sessid1, sess1 in user.sessions.data.items():
            for connid1, conn1 in sess1.connections.data.items():
                result.append(connwrap(conn1.value))
        return result

    connections = property(connections)

class sesswrap(UserDict):
    def __init__(self, sess):
        self.__get_sess = ref(sess)
        self.data = sess.data

    def connections(self):
        sess = self.__get_sess()
        if sess is None:
            raise RuntimeError('this session does not exists')
        result = [connwrap(conn1.value
            ) for connid1, conn1 in sess.connections.data.items()]
        return result

    connections = property(connections)

    def login(self, userid):
        sess = self.__get_sess()
        sess.login(userid)

    def logout(self):
        sess = self.__get_sess()
        sess.logout()

class connwrap(UserDict):
    def __init__(self, conn):
        self.__get_conn = ref(conn)
        self.data = conn.data

    def environ(self):
        conn = self.__get_conn()
        if conn is None:
            raise error(EPIPE, 'Broken pipe')
        return conn.environ

    environ = property(environ)

    def connid(self):
        conn = self.__get_conn()
        if conn is None:
            raise error(EPIPE, 'Broken pipe')
        return conn.connid

    id_ = connid = property(connid)

    def submit(self, path, args, timeout=60.):
        conn = self.__get_conn()
        if conn is None:
            raise error(EPIPE, 'Broken pipe')
        return conn.submit(path, args, timeout)

    def secure_submit(self, path, args, timeout=60.):
        conn = self.__get_conn()
        if conn is None:
            raise error(EPIPE, 'Broken pipe')
        return conn.secure_submit(path, args, timeout)

    def exec_(self, code):
        conn = self.__get_conn()
        if conn is None:
            raise error(EPIPE, 'Broken pipe')
        return conn.exec_(code)

    def secure_exec(self, code):
        conn = self.__get_conn()
        if conn is None:
            raise error(EPIPE, 'Broken pipe')
        return conn.secure_exec(code)

class globalsconnections:
    def __init__(self, mapfso):
        self.__get_mapfso = ref(mapfso)

    def __getitem__(self, connid):
        mapfso = self.__get_mapfso()
        connref = mapfso.connrefs[connid]
        conn = connref()
        return connwrap(conn)

class realuser:
    def __init__(self, userid, mapfso):
        self.sessions = limiteddict(mapfso.max_sessions)
        self.__get_mapfso = ref(mapfso)
        self.userid = userid
        self.data = {}

class realsess:
    def __init__(self, sessid, mapfso):
        self.connections = limiteddict(mapfso.max_connections)
        self.__get_mapfso = ref(mapfso)
        self.sessid = sessid
        self.data = {}

    def __del__(self):
        self.__get_mapfso().sessrefs.pop(self.sessid, None)

    def username(self):
        return self.userid

    username = property(username)

    def login(self, userid):
        if self.userid == userid:
            return
        assert userid is not None
        mapfso = self.__get_mapfso()
        sessid = self.sessid
        if self.userid is None:
            if sessid in mapfso.guests.data:
                del mapfso.guests[sessid]
        else:
            user = mapfso.users.get(self.userid)
            if user is not None and sessid in user.sessions.data:
                del user.sessions[sessid]
        user = mapfso.users.get(userid)
        if user is None:
            mapfso.users[userid] = user = realuser(userid, mapfso)
        user.sessions[sessid] = self
        self.userid = userid

    def logout(self):
        userid = self.userid
        if userid is None:
            return
        mapfso = self.__get_mapfso()
        sessid = self.sessid
        user = mapfso.users.get(userid)
        if user is not None and sessid in user.sessions.data:
            del user.sessions[sessid]
        if sessid not in mapfso.guests.data:
            mapfso.guests[sessid] = self
        self.userid = None

class realconn:
    def __init__(self, connid, mapfso, environ):
        self.__get_mapfso = ref(mapfso)
        self.environ = environ
        self.connid = connid
        self.tasks = {}
        self.data = {}
        self.buf = []

    def __del__(self):
        self.__get_mapfso().connrefs.pop(self.connid, None)

    def handshake(self):
        if 0 == self.do_handshake:
            data = jsonp_handshake_s % self.connid
            self.buf.append((None, data, 0))
            self.do_handshake = 1
            if self.back_ is not None:
                idle_switch(getcurrent(), self.back_)
        count = 1
        back_ = getcurrent()
        while self.z is None:
            timer_switch(back_, back_.parent, 2.)
            if count > 10:
                raise Timeout(ETIMEDOUT, 'Operation timed out')
            count += 1

    for secure_, data_done, data_submit, data_exec in (
        ('', '''\
    code = dumps((client_task_id, err, msg), ensure_ascii=0)
    data = jsonp_done_s % (self.connid, code)''', '''\
    code = dumps((server_task_id, path, args), ensure_ascii=0)
    data = jsonp_submit_s % (self.connid, code)''', '''\
    code = dumps(code, ensure_ascii=0)
    data = jsonp_exec_s % (self.connid, code)'''),
        ('secure_', '''\
    code = dumps((client_task_id, err, msg))
    code = b2a_base64(crypt512(self.z(), urandom(2) + code)).rstrip()
    data = jsonp_secure_done_s % (self.connid, code)''', '''\
    if self.z is None:
        self.handshake()
    code = dumps((server_task_id, path, args))
    code = b2a_base64(crypt512(self.z(), urandom(2) + code)).rstrip()
    data = jsonp_secure_submit_s % (self.connid, code)''', '''\
    if self.z is None:
        self.handshake()
    code = dumps(code)
    code = b2a_base64(crypt512(self.z(), urandom(2) + code)).rstrip()
    data = jsonp_secure_exec_s % (self.connid, code)''')):
        exec ('''\
def %(secure_)sdone(self, http, client_task_id, err, msg):
%(data_done)s
    self.buf.append((http, data, 0))
    if self.back_ is not None:
        idle_switch(getcurrent(), self.back_)
def %(secure_)ssubmit(self, path, args, timeout=60.):
    server_task_id = str(self.current_server_task_id)
    self.current_server_task_id += 1
%(data_submit)s
    self.buf.append((None, data, 0))
    self.tasks[server_task_id] = tasko = task()
    tasko.back_ = back_ = getcurrent()
    if self.back_ is not None:
        idle_switch(back_, self.back_)
    try:
        timer_switch_raise(back_, back_.parent, timeout)
    finally:
        del self.tasks[server_task_id]
    err, msg = tasko.result
    if 0 == err:
        return msg
    raise RuntimeError(err, msg)
def %(secure_)sexec_(self, code):
%(data_exec)s
    self.buf.append((None, data, 0))
    if self.back_ is not None:
        idle_switch(getcurrent(), self.back_)''' % {
    'data_submit': data_submit, 'data_done': data_done,
    'secure_': secure_, 'data_exec': data_exec })
    z = back_ = None
    secure_exec = secure_exec_
    current_server_task_id = do_handshake = 0
    del secure_, data_done, data_submit, data_exec, secure_exec_

############################################################################

def mapfsjsonp(self, http):
    environ = http.environ
    matched = is_connid(environ['QUERY_STRING'])
    if matched is None:
        return http.close(0)
    connid, initid, sessid = matched.groups()
    if initid is not None:
        assert connid is None
        if sessid is None:
            sessid, sess = self.new_realsess()
            connid, conn = self.new_realconn(sessid, environ)
            data = jsonp_init_sessid_s % (
                initid, connid, self.x_pubkey, sessid)
        else:
            sess = self.get_realsess(sessid)
            if sess is None:
                sessid, sess = self.new_realsess()
                connid, conn = self.new_realconn(sessid, environ)
                data = jsonp_init_sessid_s % (
                    initid, connid, self.x_pubkey, sessid)
            else:
                connid, conn = self.new_realconn(sessid, environ)
                data = jsonp_init_s % (initid, connid, self.x_pubkey)
        data = jsonp_h % (len(data), data)
        http.socket.write(data, 32.)
        return http.close()
    conn = self.get_realconn(connid)
    if sessid not in self.sessrefs or conn is None or conn.sessid != sessid:
        data = jsonp_drop_s % connid
        data = jsonp_h % (len(data), data)
        http.socket.write(data, 32.)
        return http.close(0)
    back_ = getcurrent()
    goto_ = conn.back_
    if goto_ is not None:
        idle_switch(back_, goto_)
        assert conn.back_ is None
    buf = conn.buf
    if 0 == len(buf):
        conn.back_ = back_
        try:
            try:
                timer_switch_raise(back_, back_.parent, self.heartbeats)
            except Timeout:
                if 0 == len(buf):
                    buf.append((None, jsonp_keep_s % connid, 0))
        finally:
            conn.back_ = None
    else:
        assert conn.back_ is None
    callers = []
    try:
        http.start_response('200 OK', jsonp_headers, 16.)
        while len(buf) > 0:
            caller, data, closed = buf.pop()
            if caller is not None:
                callers.append(caller)
            http.write(data)
        http.close()
    except:
        print_exc(file=sys.stderr)
    for caller in callers:
        try:
            caller.socket.write(accepted_h, 16.)
            caller.close()
        except:
            print_exc(file=sys.stderr)

def mapfspost(self, http):
    file_in = single(http, max_size=8192)
    name = file_in.name
    if name is None:
        return http.close(0)
    matched = get_protocol_connid(name)
    if matched is None:
        return http.close(0)
    protocol, connid = matched.groups()
    connref = self.connrefs.get(connid)
    if connref is None:
        return http.close(0)
    conn = connref()
    if '01' == protocol:
        assert conn.z is not None
        file_in = rc4_file(conn.z(), base64_file(file_in))
        file_in.read(2)
        w_args = unpack(file_in)
        done = conn.secure_done
    else:
        w_args = unpack(base64_file(file_in))
        done = conn.done
    assert isinstance(w_args, js_array)
    w_sessid = w_args.next()
    assert isinstance(w_sessid, js_string) and w_sessid.size < 128
    sessid = w_sessid.value
    if conn.sessid != sessid:
        return http.close(0)
    w_type = w_args.next()
    assert isinstance(w_type, js_string) and w_type.size < 32
    type_ = w_type.value
    if 'submit' == type_:
        assert 6 == w_args.size
        w_client_task_id = w_args.next()
        assert isinstance(w_client_task_id, js_string) and \
                    w_client_task_id.size < 64
        client_task_id = w_client_task_id.value
        w_path = w_args.next()
        assert isinstance(w_path, js_string) and 0 < w_path.size < 4096
        path = w_path.value
        w_name = w_args.next()
        assert isinstance(w_name, js_string) and 0 < w_name.size < 4096
        name = w_name.value
        module = self.findmodule(path)
        if module is None:
            return done(http, client_task_id, 2, str_ENOENT)
        func = getattr(module, 'JSRPC_' + name, None)
        if not callable(func):
            return done(http, client_task_id, 2, str_ENOENT)
        w_args = w_args.next()
        assert isinstance(w_args, js_array)
        sess = self.get_realsess(sessid)
        if sess is None:
            data = jsonp_drop_s % connid
            data = jsonp_h % (len(data), data)
            http.socket.write(data, 32.)
            return http.close(0)
        try:
            ctx = context(http, self, sess, conn)
            result = func(ctx, w_args)
        except:
            tb = StringIO()
            print_exc(file=tb)
            return done(http, client_task_id, -1, tb.getvalue())
        return done(http, client_task_id, 0, result)
    elif 'return' == type_:
        assert 6 == w_args.size
        w_client_task_id = w_args.next()
        assert isinstance(w_client_task_id, js_string) and \
                    w_client_task_id.size < 64
        client_task_id = w_client_task_id.value
        w_server_task_id = w_args.next()
        assert isinstance(w_server_task_id, js_string) and \
            0 < w_server_task_id.size < 128
        server_task_id = w_server_task_id.value
        tasko = conn.tasks.get(server_task_id)
        if tasko is None:
            return done(http, client_task_id, 22, str_EINVAL)
        goto_, tasko.back_ = tasko.back_, None
        if goto_ is None:
            return done(http, client_task_id, 22, str_EINVAL)
        w_err = w_args.next()
        assert isinstance(w_err, js_string) and 0 < w_err.size < 8
        err = w_err.value
        assert is_base10(err)
        tasko.result = (int(err, 36), w_args.next())
        idle_switch(getcurrent(), goto_)
        return done(http, client_task_id, 0, 'OK')
    elif 'handshake' == type_:
        assert 4 == w_args.size
        w_client_task_id = w_args.next()
        assert isinstance(w_client_task_id, js_string) and \
                    w_client_task_id.size < 64
        client_task_id = w_client_task_id.value
        w_key = w_args.next()
        assert isinstance(w_key, js_string) and 0 < w_key.size < 257
        key = w_key.value
        key = decrypt_from_javascript(self.prikey, self.pubkey, key)
        assert 48 == len(key)
        key1, key2 = key[0:32], key[32:48]
        conn.z = box512(key1, key2).__copy__
        return done(http, client_task_id, 0, 0)
    elif 'handshake_done' == type_:
        assert 3 == w_args.size
        w_key = w_args.next()
        assert isinstance(w_key, js_string) and 0 < w_key.size < 257
        key = w_key.value
        key = decrypt_from_javascript(self.prikey, self.pubkey, key)
        assert 48 == len(key)
        key1, key2 = key[0:32], key[32:48]
        conn.z = box512(key1, key2).__copy__
        http.socket.write(accepted_h, 16.)
        return http.close()
    else:
        return http.close(0)

class task:
    result = (-1, 'Unknow error')

for name in ('api', 'rsa'):
    exec ('''def mapfs%(name)sjs(self, http):
    environ = http.environ
    if_modified_since = environ.get('HTTP_IF_MODIFIED_SINCE')
    if if_modified_since is None:
        if_none_match = environ.get('HTTP_IF_NONE_MATCH')
        if if_none_match is not None and \
           if_none_match == apijs_etag:
            return not_modified(http)
    elif if_modified_since == apijs_last_modified:
        return not_modified(http)
    accept_encoding = environ.get('HTTP_ACCEPT_ENCODING')
    if accept_encoding is not None and 'deflate' in accept_encoding:
        http.socket.write(%(name)sjs_z_h)
    else:
        http.socket.write(%(name)sjs_h)
    http.close()''' % {'name': name})

############################################################################

def mapfsmain(self, http):
    environ = http.environ
    path_info = environ['PATH_INFO']
    if '//' in path_info:
        path_info = normpath('/', path_info)
    matched = not_forbidden(path_info)
    if matched is None:
        return forbidden(http)
    base, name = matched.groups()
    if name is None:
        name = 'index.html'
        path = path_info + 'index.html'
    else:
        hook = mapfs_hooks.get(name)
        if hook is not None:
            return hook(self, http)
        path = path_info
    cacheo = self.cache.get(path)
    if cacheo is not None:
        sessid, sess = self.get_realsess_from_environ(environ)
        if sessid is not None:
            expires = strftime(
                '%a, %d-%b-%Y %H:%M:%S GMT', gmtime(time() + 0x9660000))
            sessid = sessid_to_set_cookie % (sessid, expires)
        if_modified_since = environ.get('HTTP_IF_MODIFIED_SINCE')
        if if_modified_since is None:
            if_none_match = environ.get('HTTP_IF_NONE_MATCH')
            if if_none_match is not None and if_none_match == cacheo.etag:
                return not_modified(http, sessid)
        elif if_modified_since == cacheo.last_modified:
            return not_modified(http, sessid)
        return send_cache(http, cacheo, sessid)
    module = self.modules.get(path)
    if module is not None:
        modulemain = getattr(module, 'HTML_main', None)
        if callable(modulemain):
            sessid, sess = self.get_realsess_from_environ(environ)
            ctx = context(http, self, sess)
            if sessid is not None:
                expires = strftime(
                    '%a, %d-%b-%Y %H:%M:%S GMT', gmtime(time() + 0x9660000))
                ctx.background_response_headers = \
                    sessid_to_set_cookie % (sessid, expires)
            return modulemain(ctx)
        return not_found(http, path)
    cutoff = self.cutoff.get(path)
    if cutoff is not None:
        if NOTFOUND == cutoff:
            return not_found(http, path)
        return forbidden(http, path)
    base_  = join(self.base_, *base.split('/'))
    output = join(base_, '__DEBUG__%s.py' % name)
    code = apply_(load_source, (base_, name, output))
    if code is not None:
        code = unicode(code, 'utf-8')
        module = imp.new_module(name)
        module.__dict__.update(module_builtins)
        module.__dict__.update(self.module_builtins)
        try:
            exec code in module.__dict__
        except:
            print_exc(file=sys.stderr)
            return internal_server_error(http)
        self.modules[path] = module
        modulemain = getattr(module, 'HTML_main', None)
        if callable(modulemain):
            sessid, sess = self.get_realsess_from_environ(environ)
            ctx = context(http, self, sess)
            if sessid is not None:
                expires = strftime(
                    '%a, %d-%b-%Y %H:%M:%S GMT', gmtime(time() + 0x9660000))
                ctx.background_response_headers = \
                    sessid_to_set_cookie % (sessid, expires)
            return modulemain(ctx)
        return not_found(http, path)
    ext = splitext(name)[1].lower()
    if ext in mapfs_reserved:
        return forbidden(http, path)
    case_, data = apply_(load_file, (self, join(base_, name), ext, environ))
    if NOTMODIFIED == case_:
        return not_modified(http, path)
    elif ISFILE == case_:
        file_in, size, mimetype, if_modified_since, etag = data
        file_in_read = file_in.read
        s = http.socket
        buf = [http.environ['SERVER_PROTOCOL'] + ' 200 OK']
        sessid, sess = self.get_realsess_from_environ(environ)
        if sessid is not None:
            expires = strftime(
                '%a, %d-%b-%Y %H:%M:%S GMT', gmtime(time() + 0x9660000))
            buf.append(sessid_to_set_cookie % (sessid, expires))
        buf.append(file_headers % (mimetype, size, etag, if_modified_since))
        data = '\r\n'.join(buf)
        s.write(data, 16. + (len(data) >> 10))
        data = apply_(file_in_read, (8192, ))
        while data:
            s.write(data, 24.)
            data = apply_(file_in_read, (8192, ))
        file_in.close()
        return http.close()
    elif ISDIR == case_:
        return found(http, path_info + '/')
    elif NOTFOUND == case_ or FORBIDDEN == case_:
        self.cutoff[path] = case_
    elif NEWCACHE == case_:
        self.cache[path] = data
        sessid, sess = self.get_realsess_from_environ(environ)
        if sessid is None:
            return send_cache(http, data)
        else:
            expires = strftime(
                '%a, %d-%b-%Y %H:%M:%S GMT', gmtime(time() + 0x9660000))
            set_cookie = sessid_to_set_cookie % (sessid, expires)
            return send_cache(http, data, set_cookie)
    else:
        return internal_server_error(http)

def load_source(base_, name, output):
    try:
        return _load_source(base_, name, output)
    except:
        print_exc(file=sys.stderr)
        raise

def _load_source(base_, name, output):
    base_ = join(base_, name)
    fn = base_ + '.psp'
    if isfile(fn):
        try:
            mtime = os_stat(output).st_mtime
        except (OSError, IOError):
            mtime = 0
        if mtime and mtime > os_stat(fn).st_mtime:
            file_in = open(output, 'r')
            code = file_in.read()
            file_in.close()
            return code
        file_in = open(fn, 'r')
        code = file_in.read()
        file_in.close()
        root, ext = splitext(name)
        mimetype = _mimetypes.get(ext, 'text/html')
        code = parse(mimetype, code)
        file_out = open(output, 'w')
        file_out.write(code)
        file_out.close()
        return code
    fn = base_ + '.py'
    if isfile(fn):
        file_in = open(fn, 'r')
        code = file_in.read()
        file_in.close()
        return code

def load_file(self, fn, ext, environ):
    try:
        stat = os_stat(fn)
    except (IOError, OSError):
        return NOTFOUND, None
    if stat_S_ISDIR(stat.st_mode):
        return ISDIR, None
    try:
        file_in = open(fn, 'r')
    except (OSError, IOError):
        return FORBIDDEN, None
    last_modified = strftime(
        '%a, %d-%b-%Y %H:%M:%S GMT', gmtime(stat.st_mtime))
    etag = md5(last_modified).hexdigest()
    if_modified_since = environ.get('HTTP_IF_MODIFIED_SINCE')
    if if_modified_since is None:
        if_none_match = environ.get('HTTP_IF_NONE_MATCH')
        if if_none_match is not None and if_none_match == etag:
            return NOTMODIFIED, None
    elif if_modified_since == last_modified:
        return NOTMODIFIED, None
    size = stat.st_size
    mimetype = _mimetypes.get(ext, default_mimetype)
    if size > self.max_file_size:
        return ISFILE, (file_in, size, mimetype, if_modified_since, etag)
    cacheo = cache()
    rawdata = file_in.read(); file_in.close()
    rawlen = len(rawdata)
    cacheo.rawfn = fn = self.tmpfs_ + uuid4().get_hex()
    headers = rawheaders % (mimetype, rawlen, etag, last_modified)
    cacheo.rawlen = rawlen + len(headers)
    cacheo.rawfd = newcachefd(fn, headers, rawdata)
    cacheo.used = rawlen + len(headers)
    zdata = compress(rawdata, 9)[2:-4]
    zlen = len(zdata)
    if rawlen - zlen > 1024:
        cacheo.zfn = fn = self.tmpfs_ + uuid4().get_hex()
        headers = zheaders % (mimetype, zlen, etag, last_modified)
        cacheo.zlen = zlen + len(headers)
        cacheo.zfd = newcachefd(fn, headers, zdata)
        cacheo.used += (zlen + len(headers))
    cacheo.last_modified = last_modified
    cacheo.etag = etag
    return NEWCACHE, cacheo

def send_cache(http, cacheo, response_headers=None):
    environ = http.environ
    accept_encoding = environ.get('HTTP_ACCEPT_ENCODING')
    s = http.socket
    if response_headers is not None:
        buf = [environ['SERVER_PROTOCOL'] + ' 200 OK']
        if isinstance(response_headers, basestring):
            buf.append(response_headers)
        else:
            for pair in response_headers:
                if isinstance(pair, basestring):
                    buf.append(pair)
                else:
                    buf.append('%s: %s' % (pair[0], pair[1]))
        buf.append('')
        data = '\r\n'.join(buf)
        s.write(data, 16. + (len(data)>> 10))
        del data, buf
    else:
        s.write(environ['SERVER_PROTOCOL'] + ' 200 OK\r\n')
    if accept_encoding is not None and cacheo.zfd is not None and \
       'deflate' in accept_encoding:
        size = cacheo.zlen
        s.sendfile(cacheo.zfd, 0, size, 16. + (size >> 10))
    else:
        size = cacheo.rawlen
        s.sendfile(cacheo.rawfd, 0, size, 16. + (size >> 10))
    http.close()

def newcachefd(fn, headers, data):
    file_out = open(fn, 'w')
    file_out.write(headers)
    file_out.write(data)
    file_out.close()
    fd_in = os_open(fn, O_RDONLY)
    if fcntl(fd_in, F_SETFL, O_NONBLOCK) != 0:
        os_close(fd_in)
        errno = get_errno()
        raise OSError(errno, strerror(errno))
    return fd_in

def deldebug(base_, root):
    fn = join(base_, '__DEBUG__%s.py' % root)
    if isfile(fn):
        try:
            unlink(fn)
        except (IOError, OSError):
            pass

class cache:
    def __del__(self):
        if self.rawfd is not None:
            try:
                os_close(self.rawfd)
                unlink(self.rawfn)
            except (OSError, IOError):
                pass
        if self.zfd is not None:
            try:
                os_close(self.zfd)
                unlink(self.zfn)
            except (OSError, IOError):
                pass

    rawfd = zfd = module = None

############################################################################

class globalstorage:
    def __init__(self, mapfso):
        self.__get_mapfso = ref(mapfso)

    def __getitem__(self, key):
        mapfso = self.__get_mapfso()
        conn = self.__rpool_pop(mapfso)
        try:
            cursor = apply_(conn.execute, ('''\
SELECT v FROM o WHERE e=?''', (key, )))
            result = cursor.fetchone()
            if result is None:
                raise KeyError(key)
        finally:
            mapfso.rpool.append(conn)
        return loads(result[0])

    def __rpool_pop(self, mapfso):
        mapfso = self.__get_mapfso()
        rpool = mapfso.rpool
        if len(rpool) > 0:
            return rpool.pop()
        else:
            back_ = getcurrent()
            now = time()
            timer_switch(back_, back_.parent,
               mapfso.storage_read_idle)
            while 0 == len(rpool):
                if time() - now > mapfso.storage_read_timeout:
                    raise Timeout(ETIMEDOUT, 'Operation timed out')
                timer_switch(back_, back_.parent,
                    mapfso.storage_read_idle)
            return rpool.pop()

    def get(self, key, default=None):
        mapfso = self.__get_mapfso()
        conn = self.__rpool_pop(mapfso)
        try:
            cursor = apply_(conn.execute, ('''\
SELECT v FROM o WHERE e=?''', (key, )))
            result = cursor.fetchone()
        finally:
            mapfso.rpool.append(conn)
        if result is None:
            return default
        return loads(result[0])
        
    def search(self, p, *args):
        sql, pams, order_by = analysis(p, *args)

        mapfso = self.__get_mapfso()
        conn = self.__rpool_pop(mapfso)
        try:
            cursor = apply_(conn.execute, (sql, pams))
            ids = []
            for row in cursor.fetchall():
                ids.append(row[0])
            if len(ids) > 100000:
                return brains(conn, ids)
            else:
                return ordered_brains(conn, ids, order_by)
        finally:
            mapfso.rpool.append(conn)

class storage:
    def __init__(self, mapfso):
        wpool = mapfso.wpool
        if len(wpool) > 0:
            self.__txn = txn = wpool.pop()
        else:
            back_ = getcurrent()
            now = time()
            timer_switch(back_, back_.parent,
               mapfso.storage_connect_idle)
            while 0 == len(wpool):
                if time() - now > mapfso.storage_connect_timeout:
                    raise Timeout(ETIMEDOUT, 'Operation timed out')
                timer_switch(back_, back_.parent,
                   mapfso.storage_connect_idle)
            self.__txn = txn = wpool.pop()
        txn.changes  = set()
        txn.deletes  = set()
        txn.depends  = set()
        txn.requires = set()
        self.__closed = False
        self.__get_mapfso = ref(mapfso)

        try:
            apply_(txn.conn.rollback, ())
        except:
            pass
        mapfso.transactions[txn.id_] = txn

    def __del__(self):
        if not self.__closed:
            mapfso = self.__get_mapfso()
            mapfso.wpool.append(self.__txn)
            del mapfso.transactions[self.__txn.id_]

    def __getitem__(self, key):
        txn = self.__txn
        cursor = apply_(txn.conn.execute, ('''\
SELECT v FROM o WHERE e=?''', (key, )))
        result = cursor.fetchone()
        if result is None:
            raise KeyError(key)
        txn.depends.add(key)
        return loads(result[0])

    def __setitem__(self, key, value):
        mapfso = self.__get_mapfso()
        txn = self.__txn
        if txn.conflict:
            raise Conflict
        if key.endswith('/'):
            key = key[:-1]
        assert key.startswith('/')
        base, name = key.rsplit('/', 1)
        apply_(storage_setitem, (txn, base, name, key, value))
        txn.changes.add(key)

    def __delitem__(self, key):
        mapfso = self.__get_mapfso()
        txn = self.__txn
        if txn.conflict:
            raise Conflict
        if key.endswith('/'):
            key = key[:-1]
        assert key.startswith('/')

        apply_(storage_delitem, (txn, key))
        txn.deletes.add(key + '/')
        txn.changes.add(key)

    def close(self):
        mapfso = self.__get_mapfso()
        txn = self.__txn
        id_ = txn.id_

        if 0 == len(txn.changes):
            if not self.__closed:
                mapfso.wpool.append(txn)
                del mapfso.transactions[txn.id_]
                self.__closed = True
            return
        if txn.conflict:
            if not self.__closed:
                mapfso.wpool.append(txn)
                del mapfso.transactions[txn.id_]
                self.__closed = True
            raise Conflict
        transactions = mapfso.transactions
        for id1, txn1 in transactions.items():
            conflict = txn1.conflict
            if id_ == id1 or conflict:
                continue
            for item in txn.changes:
                if item in txn1.depends:
                    txn1.conflict = conflict = True
                    break
            if conflict:
                continue
            for item in txn.deletes:
                for jtem in txn1.requires:
                    if jtem.startswith(item):
                        txn1.conflict = conflict = True
                        break
                if conflict:
                    break

        try:
            apply_(txn.conn.commit, ())
        finally:
            mapfso.wpool.append(self.__txn)
            del mapfso.transactions[self.__txn.id_]
            self.__closed = True

    def get(self, key, default=None):
        txn = self.__txn
        cursor = apply_(txn.conn.execute, ('''\
SELECT v FROM o WHERE e=?''', (key, )))
        result = cursor.fetchone()
        if result is None:
            return default
        txn.depends.add(key)
        return loads(result[0])

    def search(self, p, *args):
        mapfso = self.__get_mapfso()
        txn = self.__txn
        sql, pams, order_by = analysis(p, *args)
        cursor = apply_(txn.conn.execute, (sql, pams))
        ids = []
        for row in cursor.fetchall():
            ids.append(row[0])
        if len(ids) > 100000:
            return brains(txn.conn, ids)
        else:
            return ordered_brains(txn.conn, ids, order_by)

class ordered_brains:
    def __init__(self, conn, ids, order_by):
        self.__data = []
        i = 0
        while i < len(ids):
            ids1 = ids[i:i+self.max_sqlite_argsize]
            if not ids1:
                break
            cursor = apply_(conn.execute, ('''\
SELECT v FROM o WHERE i IN (%s)''' % ','.join(['?'] * len(ids1)), ids1))
            for o in cursor.fetchall():
                self.__data.append(ordered_brain(loads(o[0]), order_by))
            i += self.max_sqlite_argsize
        self.__data.sort()

    def __getitem__(self, key):
        if isinstance(key, slice):
            data1 = self.__data[key]
            return [ordered_brain0.o for ordered_brain0 in data1]

        if isinstance(key, (int, long)):
            return self.__data[key].o
        raise TypeError

    def __len__(self):
        return len(self.__data)

    max_sqlite_argsize = 999

class ordered_brain:
    def __init__(self, o, order_by=None):
        self.o = o
        self.order_by = order_by

    def __lt__(self, other):
        if not self.order_by:
            return self.o < other.o
        a, b = [], []
        for field, reverse in self.order_by:
            if reverse:
                a.append(other.o[field])
                b.append(self.o [field])
            else:
                a.append(self.o [field])
                b.append(other.o[field])
        return a < b

class brains:
    def __init__(self, conn, ids):
        self.__conn = conn
        self.__ids = ids
    
    def __getitem__(self, key):
        ids = self.__ids
        conn = self.__conn
        if isinstance(key, slice):
            ids1 = ids[key]
            if not ids1:
                return []

            result = []
            i = 0
            while i < len(ids1):
                ids2 = ids1[i:i+self.max_sqlite_argsize]
                if not ids2:
                    break
                cursor = apply_(conn.execute, ('''\
SELECT v FROM o WHERE i IN (%s)''' % ','.join(['?'] * len(ids2)), ids2))
                for o in cursor.fetchall():
                    result.append(loads(o[0]))
                i += self.max_sqlite_argsize
            return result

        if isinstance(key, (int, long)):
            id_ = ids[key]
            cursor = apply_(conn.execute, ('''\
SELECT v FROM o WHERE i=?''', (id_, )))
            result = cursor.fetchone()
            return loads(result[0])
        raise TypeError

    def __len__(self):
        return len(self.__ids)

    max_sqlite_argsize = 999

def regexp(expr, data):
    return not re_match(expr, data) is None

def analysis(p, *args):
    p = pathmatch(p).groups()[0]
    sql, pams, order_by = None, list(), list()
    for i, arg in enumerate(args):
        assert isinstance(arg, (list, tuple))
        size = len(arg)
        if size == 2:
            key, value = arg
            if isinstance(value, (int, long)):
                if sql is None:
                    sql = '''\
SELECT i FROM i WHERE i.k=? AND i.v=?'''
                else:
                    sql = '''\
SELECT a.i FROM i as a INNER JOIN (%s) AS b ON a.i=b.i \
WHERE a.k=? AND a.v=?''' % sql
                pams.extend(arg)
            elif isinstance(value, basestring):
                if sql is None:
                    sql = '''\
SELECT i FROM s WHERE s.k=? AND s.v=?'''
                else:
                    sql = '''\
SELECT a.i FROM s AS a INNER JOIN (%s) AS b ON a.i=b.i \
WHERE a.k=? AND a.v=?''' % sql
                pams.extend(arg)
            elif isinstance(value, (tuple, list)):
                assert value
                if isinstance(value[0], (int, long)):
                    for value1 in value:
                        assert isinstance(value1, (int, long))

                    if sql is None:
                        sql = '''\
SELECT i FROM i WHERE i.k=? AND i.v IN (%s)
''' % ','.join(['?'] * len(value))
                    else:
                        sql = '''\
SELECT a.i FROM i AS a INNER JOIN (%s) AS b ON a.i=b.i \
WHERE a.k=? AND a.v IN (%s)''' % (sql, ','.join(['?'] * len(value)))
                    pams.append(key)
                    pams.extend(value)
                    order_by.append((key, 0))
                elif isinstance(value[0], basestring):
                    for value1 in value:
                        assert isinstance(value1, basestring)
                    if sql is None:
                        sql = '''\
SELECT i FROM s WHERE s.k=? AND s.v IN (%s)
''' % ','.join(['?'] * len(value))
                    else:
                        sql = '''\
SELECT a.i FROM s AS a INNER JOIN (%s) AS b ON a.i=b.i \
WHERE a.k=? AND a.v IN (%s)''' % (sql, ','.join(['?'] * len(value)))
                    pams.append(key)
                    pams.extend(value)
                    order_by.append((key, 0))
        elif size == 3:
            key, value1, value2 = arg
            assert type(value1) == type(value2)
            if isinstance(value1, (int, long)):
                if value1 <= value2:
                    order_by.append((key, 0))
                else:
                    arg = key, value2, value1
                    order_by.append((key, 1))
                if sql is None:
                    sql = '''\
SELECT i FROM i WHERE i.k=? AND i.v BETWEEN ? AND ?'''
                else:
                    sql = '''\
SELECT a.i FROM i AS a INNER JOIN (%s) AS b ON a.i=b.i \
WHERE a.k=? AND a.v BETWEEN ? AND ?''' % sql
                pams.extend(arg)
            elif isinstance(value1, basestring):
                if value1 <= value2:
                    order_by.append((key, 0))
                else:
                    arg = key, value2, value1
                    order_by.append((key, 1))
                if sql is None:
                    sql = '''\
SELECT i FROM s WHERE s.k=? AND s.v BETWEEN ? AND ?'''
                else:
                    sql = '''\
SELECT a.i FROM s AS a INNER JOIN (%s) AS b ON a.i=b.i \
WHERE a.k=? AND a.v BETWEEN ? AND ?''' % sql
                pams.extend(arg)
            else:
                raise ValueError
        else:
            raise ValueError

    pams.append('^%s/[^/]+$' % p)
    if sql is None:
        return '''\
SELECT i FROM o WHERE o.e REGEXP ? ORDER BY o.v;''', pams, order_by
    else:
        return '''\
SELECT o.i FROM o INNER JOIN (%s) AS b ON o.i == b.i \
WHERE o.e REGEXP ? ORDER BY o.v;''' % sql, pams, order_by

def storage_setitem(txn, base, name, key, value):
    conn = txn.conn
    if base != '':
        cursor = conn.execute('''\
SELECT i FROM o WHERE e=?''', (base, ))
        base_is_exists = cursor.fetchone()
        if base_is_exists is None:
            raise KeyError('%s/ <- %s' % (base, name))
        txn.requires.add(base + '/')
    cursor = conn.execute('''\
SELECT i FROM o WHERE e=?''', (key, ))
    result = cursor.fetchone()
    if result is None:
        cursor = conn.execute('''\
INSERT INTO o VALUES (NULL, ?, ?)''', (key, marshal_dumps(value)))
        i = cursor.lastrowid
    else:
        i = result[0]
        conn.execute('''\
DELETE FROM o WHERE i=?;''', (i, ))
        cursor = conn.execute('''\
INSERT INTO o VALUES (NULL, ?, ?)''', (key, marshal_dumps(value)))
        i = cursor.lastrowid
    if not isinstance(value, dict):
        return
    for key1, value1 in value.items():
        if isinstance(value1, (basestring)):
            conn.execute('''\
INSERT INTO s VALUES (?, ?, ?)''', (i, key1, value1))
        elif isinstance(value1, (int, long)):
            conn.execute('''\
INSERT INTO i VALUES (?, ?, ?)''', (i, key1, value1))

def storage_delitem(txn, key):
    conn = txn.conn
    conn.execute('''\
DELETE FROM o WHERE e=?''', (key, ))
    conn.execute('''\
DELETE FROM o WHERE e LIKE ?''', (key + '/%', ))

class transaction:
    conflict = False

class Conflict(IOError):
    pass

sql_script_initialize = '''\
PRAGMA journal_mode=WAL;
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS o (i INTEGER, e VARCHAR, v BLOB, \
PRIMARY KEY (i), UNIQUE (e));
CREATE INDEX IF NOT EXISTS o_i ON o (i);
CREATE INDEX IF NOT EXISTS o_e ON o (e);
CREATE TABLE IF NOT EXISTS i (i INTEGER, k VARCHAR, v INTEGER, \
FOREIGN KEY (i) REFERENCES o (i) ON DELETE CASCADE);
CREATE INDEX IF NOT EXISTS i_i ON i (i);
CREATE INDEX IF NOT EXISTS i_v ON i (v);
CREATE TABLE IF NOT EXISTS s (i INTEGER, k VARCHAR, v VARCHAR, \
FOREIGN KEY (i) REFERENCES o (i) ON DELETE CASCADE);
CREATE INDEX IF NOT EXISTS s_i ON s (i);
CREATE INDEX IF NOT EXISTS s_v ON s (v);'''

############################################################################
class mapfs:
    def __init__(self, directory, storage, tmpfs=None, salt=''):
        assert isdir(directory)
        if tmpfs is None:
            tmpfs = '/run/shm' if isdir('/run/shm') else '/dev/shm'
        self.base_ = join(directory, '')
        assert isdir(tmpfs)
        tmpfs = join(tmpfs, 'mapfs-'+uuid4().get_hex())
        mkdir(tmpfs)
        files = join(storage, 'files')
        if exists(storage):
            assert isdir(storage)
            if exists(files):
                assert isdir(files)
            else:
                mkdir(files)
        else:
            mkdir(storage)
            mkdir(files)
        self.shelf = join(storage, 'data.db')
        self.files = join(files, '')
        self.transactions = {}
        self.rpool = []
        self.wpool = []
        conn = sqlite3.connect(self.shelf)
        conn.executescript(sql_script_initialize)
        conn.commit()
        self.globalsconnections = globalsconnections(self)
        self.globalstorageo = globalstorage(self)
        for i in xrange(self.rpool_size):
            conn = sqlite3.connect(self.shelf, timeout=0, check_same_thread=0)
            conn.execute('PRAGMA foreign_keys = ON;')
            conn.create_function('REGEXP', 2, regexp)
            conn.isolation_level = None
            conn.text_factory = str
            self.rpool.append(conn)
        for i in xrange(self.wpool_size):
            conn = sqlite3.connect(self.shelf, timeout=0, check_same_thread=0)
            conn.execute('PRAGMA foreign_keys = ON;')
            conn.create_function('REGEXP', 2, regexp)
            conn.text_factory = str
            txn = transaction()
            txn.id_ = id(txn)
            txn.conn = conn
            self.wpool.append(txn)
        self.tmpfs_ = join(tmpfs, '')
        self.expiration_time = float(self.expiration_time)
        self.cache   = ordereddict(self.expiration_time)
        self.cutoff  = ordereddict(self.expiration_time)
        self.modules = ordereddict(self.expiration_time)
        self.max_file_size = int(self.max_file_size)
        self.prikey, self.pubkey = genrsa(1024)
        self.x_pubkey = '%x' % self.pubkey
        self.guests = limiteddict(self.max_guests)
        self.users  = limiteddict(self.max_users)
        self.sessrefs = {}
        self.connrefs = {}
        self.salt = salt
        self.module_builtins = {'BEGIN': self.begin,
                'STORAGE': self.globalstorageo, 
                'CONNECTIONS': self.globalsconnections}
        mapfs_refs[id(self)] = ref(self)
        if mapfswatch is not None:
            back_ = getcurrent()
            if back_.parent is None:
                greenlet(mapfswatch).switch(*(ref(self), ))
            else:
                idle_switch(back_, greenlet(mapfswatch), (ref(self), ))

    def __del__(self):
        if mapfs_refs.pop(id(self), None) is None:
            return
        try:
            rmtree(self.tmpfs_)
        except (IOError, OSError):
            pass

    __call__ = mapfsmain

    def findmodule(self, path):
        module = self.modules.get(path)
        if module is not None:
            return module
        if self.cutoff.get(path) is not None:
            return None
        try:
            base, name = path.rsplit('/', 1)
        except ValueError:
            return None
        base_ = join(self.base_, *base.split('/'))
        output = join(base_, '__DEBUG__%s.py' % name)
        code = apply_(load_source, (base_, name, output))
        if code is None:
            return None
        code = unicode(code, 'utf-8')
        module = imp.new_module(name)
        module.__dict__.update(module_builtins)
        module.__dict__.update(self.module_builtins)
        try:
            exec code in module.__dict__
        except:
            print_exc(file=sys.stderr)
            return None
        self.modules[path] = module
        return module

    def get_realsess_from_environ(self, environ):
        cookie = environ.get('HTTP_COOKIE')
        if cookie is None:
            return self.new_realsess()
        else:
            matched = sessid_from_cookie(cookie)
            if matched is None:
                return self.new_realsess()
            else:
                sessid = matched.groups()[0]
                sess = self.get_realsess(sessid)
                if sess is None:
                    return self.new_realsess()
                else:
                    return None, sess

    def new_realsess(self, userid=None):
        sessid = self.new_sessid()
        sess = realsess(sessid, self)
        sess.userid = userid
        if userid is None:
            self.guests[sessid] = sess
            self.sessrefs[sessid] = ref(sess)
            return sessid, sess
        user = self.users.get(userid)
        if user is None:
            self.users[userid] = user = realuser(userid, self)
        user.sessions[sessid] = sess
        self.sessrefs[sessid] = ref(sess)
        return sessid, sess

    def new_realconn(self, sessid, environ):
        connid = self.new_connid()
        conn = realconn(connid, self, environ)
        conn.sessid = sessid
        sessref = self.sessrefs[sessid]
        sess = sessref()
        sess.connections[connid] = conn
        self.connrefs[connid] = ref(conn)
        userid = sess.userid
        if userid is None:
            self.guests.get(sessid)
        else:
            user = self.users.get(userid)
            if user is not None:
                user.sessions.get(sessid)
        return connid, conn

    def get_realsess(self, sessid):
        sessref = self.sessrefs.get(sessid)
        if sessref is None:
            return None
        sess = sessref()
        userid = sess.userid
        if userid is None:
            sess = self.guests.get(sessid)
            assert sess is not None
            return sess
        user = self.users.get(userid)
        assert user is not None
        sess = user.sessions.get(sessid)
        assert sess is not None
        return sess

    def get_realconn(self, connid):
        connref = self.connrefs.get(connid)
        if connref is None:
            return None
        conn = connref()
        sessid = conn.sessid
        sess = self.get_realsess(sessid)
        if sess is None:
            return conn
        conn = sess.connections.get(connid)
        assert conn is not None
        return conn

    for name in ('sess', 'conn'):
        exec ('''def new_%(name)sid(self):
    a = b2a_hex(urandom(32))
    b = md5(self.salt + a).hexdigest()[:4]
    %(name)sid = b + a
    while %(name)sid in self.%(name)srefs:
        a = b2a_hex(urandom(32))
        b = md5(self.salt + a).hexdigest()[:4]
        %(name)sid = b + a
    return %(name)sid''' % {'name': name})
    del name

    def processevents(self, path, e):
        args = filter(None, path.split('/'))
        name = args.pop(-1)
        root = splitext(name)[0]
        if root.startswith('__DEBUG__'):
            return
        base_ = join(self.base_, *args)
        p, ext = splitext(path)
        if ext in ('.psp', '.py', '.pyc'):
            del self.cache[p]
            del self.modules[p]
            if IN_MODIFY == e:
                del self.cutoff[p]
            if '.psp' == ext:
                apply_(deldebug, (base_, root))
        if not path.endswith('/'):
            path = path + '/'
        del self.cache[path]
        del self.modules[path]
        if IN_MODIFY == e:
            del self.cutoff[path]

    def begin(self):
        return storage(self)

    rpool_size = 10
    wpool_size = 10
    storage_read_idle = .02
    storage_read_timeout = 8.
    storage_connect_idle = .08
    storage_connect_timeout = 8.
    max_users = max_guests = 20000
    # max_file_size = 0x200001
    max_file_size = 0 # XXX
    expiration_time = 600.
    max_connections = 8
    max_cutoff = 8192
    max_sessions = 6
    heartbeats = 90.

class ordereddict:
    def __init__(self, expiration_time=600., clean_cycle=60.):
        self.expiration_time = expiration_time
        self.head = head = genericcachenode()
        self.tail = tail = genericcachenode()
        self.expires = time() + clean_cycle
        self.clean_cycle = clean_cycle
        self.keys = blist()
        head.next = tail
        tail.prev = head
        self.data = {}

    def __setitem__(self, key, value):
        if key.endswith('/'):
            key = key[:-1]
        node = self.data.get(key)
        if node is None:
            self.data[key] = node = genericcachenode()
            keys = self.keys
            keys.insert(bisect_right(keys, key), key)
            node.key = key
        else:
            node.prev.next = node.next
            node.next.prev = node.prev
        now = time()
        node.expires = now + self.expiration_time
        node.value = value
        tail = self.tail
        node.prev = tail.prev
        node.next = tail
        tail.prev.next = node
        tail.prev = node
        if now > self.expires:
            self.garbage_clean(now)

    def __delitem__(self, key):
        now = time()
        if now > self.expires:
            self.garbage_clean(now)
        data = self.data
        keys = self.keys
        if key.endswith('/'):
            p = bisect_left(keys, key)
            while len(keys) > p:
                key1 = keys[p]
                if not key1.startswith(key):
                    break
                del keys[p]
                node = data.pop(key1)
                node.prev.next = node.next
                node.next.prev = node.prev
            key = key[:-1]
        node = data.pop(key, None)
        if node is not None:
            del keys[bisect_left(keys, key)]
            node.prev.next = node.next
            node.next.prev = node.prev

    def get(self, key, default=None):
        node = self.data.get(key)
        now = time()
        if node is None:
            if now > self.expires:
                self.garbage_clean(now)
            return default
        node.expires = now + self.expiration_time
        node.prev.next = node.next
        node.next.prev = node.prev
        tail = self.tail
        node.prev = tail.prev
        node.next = tail
        tail.prev.next = node
        tail.prev = node
        if now > self.expires:
            self.garbage_clean(now)
        return node.value

    def garbage_clean(self, now):
        self.expires = now + self.clean_cycle
        data = self.data
        keys = self.keys
        head = self.head
        node = head.next
        while node.next is not None and now > node.expires:
            key = node.key
            del keys[bisect_left(keys, key)]
            del data[key]
            node = node.next
            head.next = node
            node.prev = head

class limiteddict:
    def __init__(self, size=20000):
        self.head = head = genericcachenode()
        self.tail = tail = genericcachenode()
        head.next = tail
        tail.prev = head
        self.size = size
        self.data = {}

    def __setitem__(self, key, value):
        data = self.data
        node = data.get(key)
        if node is None:
            self.data[key] = node = genericcachenode()
            node.key = key
            if len(data) > self.size:
                head  = self.head
                first = head.next
                del data[first.key]
                first = first.next
                head.next = first
                first.prev = head
                while len(data) > self.size:
                    del data[first.key]
                    first = first.next
                    head.next = first
                    first.prev = head
        else:
            node.prev.next = node.next
            node.next.prev = node.prev
        node.value = value
        tail = self.tail
        node.prev = tail.prev
        node.next = tail
        tail.prev.next = node
        tail.prev = node

    def __delitem__(self, key):
        node = self.data.pop(key, None)
        if node is not None:
            node.prev.next = node.next
            node.next.prev = node.prev

    def get(self, key, default=None):
        node = self.data.get(key)
        if node is None:
            return default
        node.prev.next = node.next
        node.next.prev = node.prev
        tail = self.tail
        node.prev = tail.prev
        node.next = tail
        tail.prev.next = node
        tail.prev = node
        return node.value

class genericcache:
    def __init__(self, expiration_time=600., clean_cycle=60.):
        self.expiration_time = expiration_time
        self.head = head = genericcachenode()
        self.tail = tail = genericcachenode()
        self.expires = time() + clean_cycle
        self.clean_cycle = clean_cycle
        head.next = tail
        tail.prev = head
        self.data = {}

    def __setitem__(self, key, value):
        node = self.data.get(key)
        if node is None:
            self.data[key] = node = genericcachenode()
            node.key = key
        else:
            node.prev.next = node.next
            node.next.prev = node.prev
        now = time()
        node.expires = now + self.expiration_time
        node.value = value
        tail = self.tail
        node.prev = tail.prev
        node.next = tail
        tail.prev.next = node
        tail.prev = node
        if now > self.expires:
            self.garbage_clean(now)

    def __delitem__(self, key):
        now = time()
        if now > self.expires:
            self.garbage_clean(now)
        node = self.data.pop(key, None)
        if node is not None:
            node.prev.next = node.next
            node.next.prev = node.prev

    def get(self, key, default=None):
        node = self.data.get(key)
        now = time()
        if node is None:
            if now > self.expires:
                self.garbage_clean(now)
            return default
        node.expires = now + self.expiration_time
        node.prev.next = node.next
        node.next.prev = node.prev
        tail = self.tail
        node.prev = tail.prev
        node.next = tail
        tail.prev.next = node
        tail.prev = node
        if now > self.expires:
            self.garbage_clean(now)
        return node.value

    def garbage_clean(self, now):
        self.expires = now + self.clean_cycle
        data = self.data
        head = self.head
        node = head.next
        while node.next is not None and now > node.expires:
            del data[node.key]
            node = node.next
            head.next = node
            node.prev = head

class genericcachenode:
    next = prev = None

############################################################################

class form:
    exec ('''def __init__(self, http, max_size=0x100000, timeout=-1):
    environ = http.environ
    left    = http.left
    if 0 == left:
        self.salt = salt = urandom(4)
        self.data = data = {}
        for item in environ['QUERY_STRING'].split('&'):%(s)s
    elif left > max_size:
        raise ValueError('length > %%i' %% max_size)
    else:
        if -1 == timeout:
            timeout = 16. + (left >> 10)
        data = http.read(left, timeout)
        self.salt = salt = urandom(8)
        self.data = data = {}
        for item in environ['QUERY_STRING'].split('&'):%(s)s
        for item in data.split('&'):%(s)s''' % {'s': '''
            pair = item.split('=', 1)
            if len(pair) != 2:
                continue
            k, v  = pair
            k = salt + unquote_plus(k)
            value = data.get(k)
            if value is None:
                data[k] = unquote_plus(v)
            elif isinstance(value, list):
                data[k].append(unquote_plus(v))
            else:
                data[k] = [value, unquote_plus(v)]''' })

    def __getitem__(self, key):
        return self.data[self.salt + key]

    def __contains__(self, key):
        return (self.salt + key) in self.data

    def get(self, key, default=None):
        return self.data.get(self.salt+key, default)

    def has_key(self, key):
        return (self.salt + key) in self.data

code = r'''def read(self, size, timeout=-1):
    if 0 == self.left:
        if self.closed:
            return ''
        assert self.nl   == self.http.read(self.nlen)
        assert self.stop == self.http.read(self.stoplen)
        self.closed = 1
        return ''
    if size > self.left:
        if -1 == timeout:
            timeout = 16. + (self.left >> 10)
        data = self.http.read(self.left, timeout)
        self.left = 0
    else:
        if -1 == timeout:
            timeout = 16. +  (size >> 10)
        data = self.http.read(size, timeout)
        self.left -= len(data)
    if 0 == self.left:
        self.closed = 1
        assert self.nl   == self.http.read(self.nlen)
        assert self.stop == self.http.read(self.stoplen)
    return data
def readline(self, size, timeout=-1):
    if 0 == self.left:
        if self.closed:
            return ''
        assert self.nl   == self.http.read(self.nlen)
        assert self.stop == self.http.read(self.stoplen)
        self.closed = 1
        return ''
    if size > self.left:
        if -1 == timeout:
            timeout = 16. + (self.left >> 10)
        data = self.http.readline(self.left, timeout)
    else:
        if -1 == timeout:
            timeout = 16. + (size >> 10)
        data = self.http.readline(size, timeout)
    self.left -= len(data)
    if 0 == self.left:
        self.closed = 1
        assert self.nl   == self.http.read(self.nlen)
        assert self.stop == self.http.read(self.stoplen)
    return data
def close(self, timeout=-1):
    if 0 == self.left:
        if self.closed:
            return
        assert self.nl   == self.http.read(self.nlen)
        assert self.stop == self.http.read(self.stoplen)
        self.closed = 1
        return
    if -1 == timeout:
        timeout = 16. + (len(self.left) >> 10)
    time0 = time()
    while self.left > 0:
        dummy = self.http.read(min(self.left, 8192), timeout)
        timeout -= time() - time0
        self.left -= len(dummy)
    assert self.nl   == self.http.read(self.nlen)
    assert self.stop == self.http.read(self.stoplen)
    self.closed = 1
nl, nlen = '\r\n', 2
filename = name = None
size = left = closed = 0'''

class multipart:
    def __init__(self, http, max_size=0x100000, timeout=-1):
        self.salt = salt = urandom(4)
        self.fields = fields = {}
        if 0 == http.left:
            return
        boundary = '--' + parse_header(
            http.environ['CONTENT_TYPE'])[1]['boundary']
        length = http.left
        if -1 == timeout:
            timeout = 16. + (max_size >> 10)
        time0 = time()
        data = http.readline(8192, timeout)
        timeout -= time() - time0
        if length - http.left > max_size:
            raise ValueError('length > %i' % max_size)
        if '\r\n' == data[-2:]:
            nlen = 2
            nl = '\r\n'
            self.next = next = boundary +   '\r\n'
            self.stop = stop = boundary + '--\r\n'
            self.nextlen = nextlen = len(self.next)
            self.stoplen = stoplen = len(self.stop)
        else:
            assert '\n' == data[-1]
            self.nl = nl = '\n'
            self.nlen = nlen = 1
            self.next = next = boundary +   '\n'
            self.stop = stop = boundary + '--\n'
        self.nextlen = nextlen = len(self.next)
        assert nextlen == len(data) and data == next
        self.stoplen = stoplen = len(self.stop)
        while not self.closed:
            environ = {}
            data = http.readline(8192, timeout)
            timeout -= time() - time0
            if length - http.left > max_size:
                raise ValueError('length > %i' % max_size)
            while 1:
                matched = is_header(data)
                if matched is None:
                    if '\r\n' == data or '\n' == data:
                        break
                    raise ValueError(
                        'invalid http header %r' % data)
                key, val = matched.groups()
                environ['HTTP_' + key.upper().replace('-', '_')] = val
                data = http.readline(8192, 24.)
            key, val = parse_header(environ['HTTP_CONTENT_DISPOSITION'])
            assert key.lower() == 'form-data'
            name = val['name']
            if 'filename' in val:
                self.http = http
                self.name = name
                self.filename = val['filename']
                self.size = self.left = http.left - nlen - stoplen
                break
            buf  = StringIO()
            data = http.readline(8192, timeout)
            timeout -= time() - time0
            while 1:
                if not data:
                    raise ValueError('bad request')
                elif len(data) == nextlen and data == next:
                    break
                elif len(data) == stoplen and data == stop:
                    self.closed = 1
                    break
                buf.write(data)
                data = http.readline(8192, timeout)
                timeout -= time() - time0
            buf.seek(-nlen, 2)
            assert nl == buf.read()
            size = buf.tell() - nlen
            key = salt + name
            val = fields.get(key)
            buf.seek(0)
            data = buf.read(size)
            if val is None:
                fields[key] = data
            elif isinstance(val, list):
                fields[key].append(data)
            else:
                fields[key] = [val, data]

    def __getitem__(self, key):
        return self.fields[self.salt+key]

    def __contains__(self, key):
        return self.salt + key in self.fields

    def has_key(self, key):
        return self.salt + key in self.fields

    def get(self, key, default=None):
        return self.fields.get(self.salt+key, default)

    exec(code)

class single:
    def __init__(self, http, max_size=8192, timeout=-1):
        if 0 == http.left:
            return
        boundary = '--' + parse_header(
            http.environ['CONTENT_TYPE'])[1]['boundary']
        length = http.left
        if -1 == timeout:
            timeout = 16. + (max_size >> 10)
        time0 = time()
        data = http.readline(8192, timeout)
        timeout -= time() - time0
        if length - http.left > max_size:
            raise ValueError('length > %i' % max_size)
        if '\r\n' == data[-2:]:
            nlen = 2
            nl = '\r\n'
            self.next = next = boundary +   '\r\n'
            self.stop = stop = boundary + '--\r\n'
            self.nextlen = nextlen = len(self.next)
            self.stoplen = stoplen = len(self.stop)
        else:
            assert '\n' == data[-1]
            self.nl = nl = '\n'
            self.nlen = nlen = 1
            self.next = next = boundary +   '\n'
            self.stop = stop = boundary + '--\n'
        self.nextlen = nextlen = len(self.next)
        assert nextlen == len(data) and data == next
        self.stoplen = stoplen = len(self.stop)
        environ = {}
        data = http.readline(8192, timeout)
        timeout -= time() - time0
        if length - http.left > max_size:
            raise ValueError('length > %i' % max_size)
        while 1:
            matched = is_header(data)
            if matched is None:
                if '\r\n' == data or '\n' == data:
                    break
                raise ValueError('invalid http header %r' % data)
            key, val = matched.groups()
            environ['HTTP_' + key.upper().replace('-', '_')] = val
            data = http.readline(8192, 24.)
        key, val = parse_header(environ['HTTP_CONTENT_DISPOSITION'])
        assert key.lower() == 'form-data'
        if 'filename' in val:
            self.filename = val['filename']
        self.http = http
        self.name = val['name']
        self.size = self.left = http.left - nlen - stoplen

    exec(code)

############################################################################

def parse(mimetype, code):
    result, extbuf, lvnm = deque(), deque(), []
    lv = lvif = lvfor = lvdef = nil = 0
    for case_, data in tokenize(code):
        if case_ == TEXT:
            extbuf.append(u"r'''%s'''" % data)
        elif case_ == EXPR:
            extbuf.append(u'STR(%s)' % data)
        if extbuf:
            if lvdef > 0:
                line = WRITELINES % (u', '.join(extbuf))
                result.append(INDENT * lv + line)
                nil = 0
            extbuf = deque()
        if case_ == IF:
            result.append(INDENT * lv + data)
            lv, lvif, nil = lv + 1, lvif + 1, 1
        elif case_ == ELSE or case_ == ELIF:
            lv, lvif = lv - 1, lvif - 1
            if -1 == lvif:
                raise SyntaxError(u'invalid syntax %r' % data)
            result.append(INDENT * lv + data)
            lv, lvif, nil = lv + 1, lvif + 1, nil
        elif case_ == FOR:
            result.append(INDENT * lv + data)
            lv, lvfor, nil = lv + 1, lvfor + 1, nil
        elif case_ == ENDIF:
            if nil:
                result.append(INDENT * lv + 'pass')
            lv, lvif, nil = lv - 1, lvif - 1, 0
            if -1 == lvif:
                raise SyntaxError(u'invalid syntax %r' % data)
        elif case_ == ENDFOR:
            if nil:
                result.append(INDENT * lv + 'pass')
            lv, lvfor, nil = lv - 1, lvfor - 1, 0
            if -1 == lvfor:
                raise SyntaxError(u'invalid syntax %r' % data)
        elif case_ == PY:
            indent = None
            nil = p = 0
            for line in data.split(u'\n'):
                space, code = template_indent(line).groups()
                if not code:
                    continue
                if indent is None:
                    indent = space
                    p = len(space)
                    result.append(INDENT * lv + code)
                elif space[:p] == indent:
                    result.append(INDENT * lv + line[p:])
                else:
                    raise IndentationError(u'unexpected indent %r' % line)
        elif case_ == DEF:
            matched = template_1starg(data)
            if matched is None:
                raise SyntaxError(u'"%r" needs at least 1 argument' % data)
            name, arg = matched.groups()
            result.append(u'\n%sdef HTML_%s:' % (INDENT * lv, data))
            lv, lvdef = lv + 1, lvdef + 1
            lvnm.append(name)
            indent = INDENT * lv
            if 'main' == name:
                for line in (FUNCHEADMAIN % (arg, arg, mimetype)
                    ).split('\n'):
                    result.append(indent + line)
            else:
                for line in (FUNCHEAD % (arg, arg)).split('\n'):
                    result.append(indent + line)
        elif case_ == ENDDEF:
            if lvif != 0:
                raise SyntaxError('lost "%endif"')
            if lvfor != 0:
                raise SyntaxError('lost "%endfor"')
            indent = INDENT * lv
            if 'main' == lvnm.pop(-1):
                for line in FUNCTAILMAIN.split('\n'):
                    result.append(indent + line)
            else:
                for line in FUNCTAIL.split('\n'):
                    result.append(indent + line)
            lv, lvdef, nil = lv - 1, lvdef - 1, 0
            if -1 == lvdef:
                raise SyntaxError(u'invalid syntax %r' % data)
        elif case_ == INCLUDE:
            path, expr = template_include(data).groups()
            u_expr = unicode(eval(expr), 'utf-8')
            matched = template_incargs(u_expr)
            if matched is None:
                raise SyntaxError(u'invalid syntax %r' % data)
            name, args = matched.groups()
            if args is None:
                args = u'CTX'
            else:
                args = args.strip()
                if not args:
                    args = u'CTX'
                else:
                    args = u'CTX, ' + args
            if path is None:
                data = u'HTML_%s(%s)' % (name, args)
            else:
                path = path.strip()
                if not path:
                    data = u'HTML_%s(%s)' % (name, args)
                else:
                    data = u'CTX.findmodule(%s).HTML_%s(%s)' % (
                                    path, name, args)
            result.append(u'%s%s' % (INDENT * lv, data))
    if lvif != 0:
        raise SyntaxError('lost "%endif"')
    if lvfor != 0:
        raise SyntaxError('lost "%endfor"')
    if lvdef != 0:
        raise SyntaxError('lost "</%def>"')
    result.appendleft(MODULEHEAD)
    return u'\n'.join(result).encode('utf-8')

def tokenize(code):
    code = unicode(code, 'utf-8')
    matched = template_search(code)
    buf, p = deque(), 0
    while matched is not None:
        text = code[p:matched.start()]
        if text:
            buf.append(text)
        g1, g2, g3, g4, g5, g6, g7 = matched.groups()
        if g1 is not None:
            buf.append(g1[0])
            p = matched.end()
            matched = template_search(code, p)
            continue
        if buf:
            yield (TEXT, u''.join(buf))
            buf = deque()
        if g2 is not None:
            yield (EXPR, g2)
        elif g3 is not None:
            istag = template_tags(g3)
            if istag is None:
                raise SyntaxError(g3)
            groups = istag.groups()
            for i in xrange(len(groups)):
                if groups[i] is not None:
                    yield (IF + i, g3)
                    break
            else:
                raise SyntaxError(g3)
        elif g4 is not None:
            u_g4 = unicode(eval(g4).strip(), 'utf-8')
            yield (DEF, u_g4)
        elif g5 is not None:
            yield (ENDDEF, None)
        elif g6 is not None:
            yield (INCLUDE, g6)
        else:
            p1 = matched.end()
            matched = template_closcr(code, p1)
            if matched is None:
                raise SyntaxError('lost "%>"')
            while 1:
                if matched.groups()[0] is not None:
                    yield (PY, code[p1:matched.start()])
                    break
                p = matched.end()
                matched = template_closcr(code, p)
                if matched is None:
                    raise SyntaxError('lost "%>"')
        p = matched.end()
        matched = template_search(code, p)
    text = code[p:]
    if text:
        buf.append(text)
    if buf:
        yield (TEXT, u''.join(buf))

def STR(s):
    if isinstance(s, basestring):
        return s
    elif isinstance(s, (list, tuple, dict)):
        return dumps(s)
    try:
        return dumps(list(s))
    except:
        try:
            return str(s)
        except:
            return repr(s)

############################################################################

class base64_file:
    def __init__(self, file_in):
        self.file_in = file_in
        self.rbuf_len = 0
        self.rbuf = ''

    def read(self, size):
        rbuf_len = self.rbuf_len
        if rbuf_len >= size:
            if rbuf_len > size:
                if 0 == size:
                    return ''
                rbuf, self.rbuf = self.rbuf
                self.rbuf_len = 1
                return rbuf
            rbuf, self.rbuf = self.rbuf, ''
            self.rbuf_len = 0
            return rbuf
        n = size - rbuf_len
        a, b = divmod(n, 3)
        if 0 == b:
            data = a2b_base64(self.file_in.read(a << 2))
            assert len(data) == n
            if 0 == rbuf_len:
                return data
            rbuf, self.rbuf = self.rbuf, ''
            self.rbuf_len = 0
            return rbuf + data
        data = a2b_base64(self.file_in.read((a << 2) + 4))
        n = len(data) - n
        assert n > -1
        if 0 == n:
            if 0 == rbuf_len:
                return data
            rbuf, self.rbuf = self.rbuf, ''
            self.rbuf_len = 0
            return rbuf + data
        if 1 == n:
            if 0 == rbuf_len:
                self.rbuf = data[-1:]
                self.rbuf_len = 1
                return data[:-1]
            else:
                rbuf, self.rbuf = self.rbuf, data[-1:]
                self.rbuf_len = 1
                return rbuf + data[:-1]
        elif 0 == rbuf_len:
            self.rbuf = data[-2:]
            self.rbuf_len = 2
            return data[:-2]
        else:
            rbuf, self.rbuf = self.rbuf, data[-2:]
            self.rbuf_len = 2
            return rbuf + data[:-2]

class rc4_file:
    def __init__(self, z, file_in):
        self.file_in = file_in
        self.z = z

    def read(self, size):
        return crypt512(self.z, self.file_in.read(size))

def unpack(file_in):
    c = file_in.read(1)
    if '[' == c:
        s = file_in.read(4)
        assert is_base36(s)
        return js_array(file_in, int(s, 36))
    s = c + file_in.read(3)
    assert is_base36(s)
    return js_string(file_in, int(s, 36))

class js_array:
    def __init__(self, file_in, size):
        self.size = self.left = size
        self.file_in = file_in

    def __iter__(self):
        return self

    def next(self):
        if self.left <= 0:
            raise StopIteration
        file_in = self.file_in
        c = file_in.read(1)
        self.left -= 1
        if '[' == c:
            s = file_in.read(4)
            assert is_base36(s)
            return js_array(file_in, int(s, 36))
        s = c + file_in.read(3)
        assert is_base36(s)
        return js_string(file_in, int(s, 36))

    is_array = True

class js_string:
    def __init__(self, file_in, size):
        self.file_in = file_in
        self.size = size

    def getvalue(self):
        if self._value is None:
            data = self.file_in.read(self.size)
            assert len(data) == self.size
            self._value = data
            return data
        return self._value

    is_array = False
    _value, value = None, property(getvalue)

############################################################################

def genrsa(bits=1024):
    result = Popen(['openssl', 'genrsa', '%d' % bits],
            stdin=PIPE, stdout=PIPE, stderr=PIPE)
    data = result.stdout.read();  result.stdout.close()
    result = Popen(['openssl', 'rsa', '-text', '-noout'],
            stdin=PIPE, stdout=PIPE, stderr=PIPE)
    result.stdin.write(data);  result.stdin.close()
    data = result.stdout.read();  result.stdout.close()
    matched = rsa_text_noout(data)
    if matched is None:
        raise RuntimeError('openssl unknow error')
    i, n, i, e, i, i, i, i, i = matched.groups()
    e = int(''.join(e.split()).replace(':', ''), 16)
    n = int(''.join(n.split()).replace(':', ''), 16)
    return e, n

def rsaopen(fn):
    file_in = open(fn, 'r')
    data = file_in.read();  file_in.close()
    result = Popen(['openssl', 'rsa', '-text', '-noout'],
            stdin=PIPE, stdout=PIPE, stderr=PIPE)
    result.stdin.write(data);  result.stdin.close()
    data = result.stdout.read();  result.stdout.close()
    matched = rsa_text_noout(data)
    if matched is None:
        raise RuntimeError('openssl unknow error')
    i, n, i, e, i, i, i, i, i = matched.groups()
    e = int(''.join(e.split()).replace(':', ''), 16)
    n = int(''.join(n.split()).replace(':', ''), 16)
    return e, n

def encrypt(pubkey, s):
    i = pow(int(b2a_hex(s), 16), 0x10001, pubkey)
    result = array('c')
    while i:
        i, c = divmod(i, 256)
        result.append(chr(c))
    return ''.join(reversed(result))

def decrypt(prikey, pubkey, s):
    i = pow(int(b2a_hex(s), 16), prikey, pubkey)
    result = array('c')
    while i:
        i, c = divmod(i, 256)
        result.append(chr(c))
    return ''.join(reversed(result))

def decrypt_from_javascript(prikey, pubkey, s):
    i = pow(int(s, 16), prikey, pubkey)
    result = array('c')
    while i:
        i, c = divmod(i, 256)
        result.append(chr(c))
    return result.tostring()

def box512(s1, s2):
    z = range516()
    x11 = x21 = 0
    y1 = len(s1)
    y2 = len(s2)
    for i in xrange(256):
        j = i + 258
        x11 = (x11 + z[i] + ord(s1[i % y1])) % 256
        x21 = (x21 + z[j] + ord(s2[i % y2])) % 256
        x22 = x21 + 258
        z[i], z[x11], z[j], z[x22] = z[x11], z[i], z[x22], z[j]
    return z

def crypt512(z, s):
    r = array('c')
    x11, y11, x21, y21 = z[256], z[257], z[514], z[515]
    for c in s:
        x11 = (x11 + 1) % 256
        x21 = (x21 + 1) % 256
        x22 = x21 + 258
        y11 = (y11 + z[x11]) % 256
        y21 = (y21 + z[x22]) % 256
        y22 = y21 + 258
        z[x11], z[y11], z[x22], z[y22] = z[y11], z[x11], z[y22], z[x22]
        r.append(chr(ord(c) ^ z[(z[x11] + z[y11]) % 256] ^ \
                    z[(z[x22] + z[y22]) % 256 + 258]))
    z[256], z[257], z[514], z[515] = x11, y11, x21, y21
    return r.tostring()

def box(s):
    z = range258()
    y = len(s)
    x = 0
    for i in xrange(256):
        x = (x + z[i] + ord(s[i % y])) % 256
        z[i], z[x] = z[x], z[i]
    return z

def crypt(z, s):
    x = z[256]
    y = z[257]
    r = array('c')
    for c in s:
        x = (x + 1) % 256
        y = (y + z[x]) % 256
        z[x], z[y] = z[y], z[x]
        r.append(chr(ord(c) ^ z[(z[x] + z[y]) % 256]))
    z[256] = x
    z[257] = y
    return r.tostring()

############################################################################

import eurasia, platform
platform = 'mapfs-%s / eurasia-%s / python-%s:%s' % (
    __version__, eurasia.__version__, platform.python_version(),
                    platform.python_revision())
defaults = {}
_200_s = '''<html><head><title>Default Page</title></head><body><h1>It work\
s!</h1><hr /><address>%s</address></body></html>''' % platform
_200_h = '''Content-Type: text/html\r\nContent-Length: %d\r\n\r\n%s\
''' % (len(_200_s), _200_s)

def default_page(http, response_headers=None):
    buf = [http.environ['SERVER_PROTOCOL'] + ' 200 OK']
    if response_headers is not None:
        if isinstance (response_headers, str):
            buf.append(response_headers)
        else:
            for pair in response_headers:
                if isinstance (pair, str):
                    buf.append(pair)
                else:
                    buf.append('%s: %s' % pair)
    buf.append(_200_h)
    data = '\r\n'.join(buf)
    http.socket.write(data, 16. + (len(data)>>10))
    http.close()

defaults[200] = default_page

def not_modified(http, response_headers=None):
    buf = [http.environ['SERVER_PROTOCOL'] + ' 304 Not Modified']
    if response_headers is not None:
        if isinstance (response_headers, str):
            buf.append(response_headers)
        else:
            for pair in response_headers:
                if isinstance (pair, str):
                    buf.append(pair)
                else:
                    buf.append('%s: %s' % pair)
    buf.append('\r\n')
    data = '\r\n'.join(buf)
    http.socket.write(data, 16. + (len(data) >> 10))
    http.close()

defaults[304] = not_modified
_403_s = '''\<html><head><title>403 Forbidden</title></head><body><h1>Forbi\
dden</h1><p>You don\'t have permission to list the contents of %%s on this \
server.</p><hr /><address>%s</address></body></html>''' % platform
_403_h = '''Content-Type: text/html\r\nContent-Length: %d\r\n'''

def forbidden(http, path=None, response_headers=None):
    environ = http.environ
    if path is None:
        s = _403_s % quote(environ['SCRIPT_NAME'] + environ['PATH_INFO'])
    else:
        s = _403_s % quote(path)
    buf = [environ['SERVER_PROTOCOL'] + ' 403 Forbidden']
    if response_headers is not None:
        if isinstance (response_headers, str):
            buf.append(response_headers)
        else:
            for pair in response_headers:
                if isinstance (pair, str):
                    buf.append(pair)
                else:
                    buf.append('%s: %s' % pair)
    buf.append(_403_h % len(s))
    buf.append(s)
    data = '\r\n'.join(buf)
    http.socket.write(data, 16. + (len(data) >> 10))
    http.close()

defaults[403] = forbidden
_404_s = '''<html><head><title>404 Not Found</title></head><body><h1>Not Fo\
und</h1><p>The requested URL %%s was not found on this server.</p><hr /><ad\
dress>%s</address></body></html>''' % platform
_404_h = '''Content-Type: text/html\r\nContent-Length: %d\r\n'''

def not_found(http, path=None, response_headers=None):
    environ = http.environ
    if path is None:
        s = _404_s % quote(environ['SCRIPT_NAME'] + environ['PATH_INFO'])
    else:
        s = _404_s % quote(path)
    buf = [environ['SERVER_PROTOCOL'] + ' 404 Not Found']
    if response_headers is not None:
        if isinstance (response_headers, str):
            buf.append(response_headers)
        else:
            for pair in response_headers:
                if isinstance (pair, str):
                    buf.append(pair)
                else:
                    buf.append('%s: %s' % pair)
    buf.append(_404_h % len(s))
    buf.append(s)
    data = '\r\n'.join(buf)
    http.socket.write(data, 16. + (len(data) >> 10))
    http.close()

defaults[404] = not_found
_500_s = '''<html><head><title>500 Internal Server Error</title></head><bod\
y><h1>Internal Server Error</h1><p>The server encountered an internal error\
 and was unable to complete your request.</p><hr /><address>%s</address></b\
ody></html>''' % platform
_500_h = '''Content-Type: text/html\r\nContent-Length: %d\r\n\r\n%s\
''' % (len(_500_s), _500_s)

def internal_server_error(http, response_headers=None):
    buf = [http.environ['SERVER_PROTOCOL'] + ' 500 Internal Server Error']
    if response_headers is not None:
        if isinstance (response_headers, str):
            buf.append(response_headers)
        else:
            for pair in response_headers:
                if isinstance (pair, str):
                    buf.append(pair)
                else:
                    buf.append('%s: %s' % pair)
    buf.append(_500_h)
    data = '\r\n'.join(buf)
    http.socket.write(data, 16. + (len(data)>>10))
    http.close()

defaults[500] = server_error = internal_server_error
_30X_h = '''\
Content-Type: text/html\r\nContent-Length: %d\r\nLocation: %s\r\n'''
_30X_s = '''<html><head><title>%%s</title></head><body><h1>%%s</h1><ul>%%%%\
s</ul><hr /><address>%s</address></body></html>''' % platform
_30X_i = '<li><a href="%s">%s</a></li>\r\n'
_30X_u = '''<html><head><title>%%s</title></head><body><p>%%s <a href="%%%%\
s">%%%%s</a></p><hr /><address>%s</address></body></html>''' % platform

code = '''\
_%(num)d_s = _30X_s %% (%(title)r, %(h1)r)
_%(num)d_u = _30X_u %% (%(title)r, %(p)r)
def %(name)s(http, urls=None, response_headers=None):
    environ = http.environ
    server_port = environ.get('SERVER_PORT', 80)
    host = environ.get('HTTP_HOST')
    assert host is not None
    is_https = environ.get('HTTPS', 'off') in ('on', '1', 1)
    if 80 == server_port:
        if is_https:
            base_ = 'https://%%s/' %% host
        else:
            base_ = 'http://%%s/' %% host
    elif is_https:
        base_ = 'https://%%s:%%d/' %% (host, server_port)
    else:
        base_ = 'http://%%s:%%d/' %% (host, server_port)
    if urls is None:
        urls = environ['SCRIPT_NAME'] + environ['PATH_INFO']
        assert not urls.endswith('/')
        urls = urls + '/'
    buf = ['%%s %(status)s' %% http.environ['SERVER_PROTOCOL']]
    if response_headers is not None:
        if isinstance (response_headers, str):
            buf.append(response_headers)
        else:
            for pair in response_headers:
                if isinstance (pair, str):
                    buf.append(pair)
                else:
                    buf.append('%%s: %%s' %% pair)
    if isinstance(urls, str):
        p = urls.rfind('/')
        if -1 == p:
            urls = base_ + urls
        else:
            args = []
            for arg in urls[:p].split('/'):
                if '..' == arg:
                    if len(args) > 0:
                        args.pop()
                elif '' == arg or '.' == arg:
                    continue
                else:
                    args.append(arg)
            urls = '%%s%%s%%s' %% (base_, '/'.join(args), urls[p:])
        s = _%(num)d_u %% (urls, quote(urls))
        buf.append(_30X_h %% (len(s), urls))
        buf.append(s)
        data = '\\r\\n'.join(buf)
        http.socket.write(data, 16. + (len(data) >> 10))
        http.close()
    uli = []
    for url in urls:
        p = url.rfind('/')
        if -1 == p:
            url = base_ + url
        else:
            args = []
            for arg in url[:p].split('/'):
                if '..' == arg:
                    if len(args) > 0:
                        args.pop()
                elif '' == arg or '.' == arg:
                    continue
                else:
                    args.append(arg)
            url = '%%s%%s%%s' %% (base_, '/'.join(args), url[p:])
        uli.append(url)
    s = ''.join(_30X_i %% (url, quote(url)) for url in uli)
    s = _%(num)d_s %% s
    buf.append(_30X_h %% (len(s), uli[0]))
    buf.append(s)
    data = '\\r\\n'.join(buf)
    http.socket.write(data, 16. + (len(data) >> 10))
    http.close()
defaults[%(num)d] = %(name)s'''

for lines in ['''300 Multiple Choices
Object has several resources
Object has several resources -- see URI list
This resource can be found at''',
'''301 Moved Permanently
Object moved permanently
Object moved permanently -- see URI list
This resource has permanently moved to''',
'''302 Found
Object moved temporarily
Object moved temporarily -- see URI list
This resource resides temporarily at''',
'''303 See Other
Object moved
Object moved -- see Method and URL list
This resource can be found at''',
'''307 Temporary Redirect
Object moved temporarily
Object moved temporarily -- see URI list
This resource has moved temporarily to''']:
    status, title, h1, p = tuple(line.strip() for line in lines.split('\n'))
    num, name = status.split(None, 1)
    num = int(num)
    name = '_'.join(s.lower() for s in filter(None, name.split(None)))
    exec code % { 'h1': h1, 'name': name, 'p': p, 'num': num,
                      'status': status, 'title': title }

def quote(s):
    return s.replace('<', '&lt;').replace('>', '&gt;')

############################################################################

import atexit
def mapfscleantmpfs():
    while mapfs_refs and len(mapfs_refs) > 0:
        try:
            id_, get_mapfs = mapfs_refs.popitem()
        except KeyError:
            break
        mapfsobj = get_mapfs()
        if mapfsobj is not None:
            try:
                rmtree(mapfsobj.tmpfs_)
            except (IOError, OSError):
                pass
mapfs_refs = {}
atexit.register(mapfscleantmpfs)

try:
    import pyinotify
except ImportError:
    mapfswatch = None
else:
    def mapfswatch(get_mapfs):
        watcher = pyinotify.WatchManager()
        handler = ProcessEvent(get_mapfs)
        notifier = pyinotify.Notifier(watcher, handler)
        watcher.add_watch(get_mapfs().base_, mapfswatchmask, rec=True)
        while get_mapfs() is not None:
            if apply_(notifier.check_events):
                notifier.read_events()
                notifier.process_events()

    class ProcessEvent(pyinotify.ProcessEvent):
        def __init__(self, get_mapfs):
            pyinotify.ProcessEvent.__init__(self)
            self.get_mapfs = get_mapfs

        for func in ('modify', 'delete'):
            exec '''def process_IN_%s(self, event):
    mapfso = self.get_mapfs()
    if mapfso is not None:
        fn = join(event.path, event.name)
        if not fn.startswith(mapfso.base_):
            return
        args = fn[len(mapfso.base_):].split(sep)
        key = '/' + '/'.join(filter(None, args))
        mapfso.processevents(key, IN_%s)''' % (
                func.upper(), func.upper())
        process_IN_CREATE = process_IN_MOVED_TO = process_IN_MODIFY
        process_IN_MOVED_FROM = process_IN_DELETE

    mapfswatchmask = eval('|'.join('pyinotify.IN_%s' % s for s in [
        'CREATE', 'DELETE', 'MODIFY', 'MOVED_FROM', 'MOVED_TO']))
    IN_MODIFY, IN_DELETE = pyinotify.IN_MODIFY, pyinotify.IN_DELETE

import mimetypes
if not mimetypes.inited:
    mimetypes.init()
_mimetypes = mimetypes.types_map.copy()
default_mimetype = 'application/octet-stream'

from array import array
range258 = range(258)
range258[256] = range258[257] = 0
range258 = array('i', range258).__copy__
range516 = range(516)
for i in xrange(256, 516):
    range516[i] -= 258
range516[256] = range516[257] = range516[514] = range516[515] = 0
range516 = array('i', range516).__copy__

import re
from re import match as re_match
string = (ur'(?:"(?:(?:\\\\)?(?:\\")?[^"]?)*")?'
          ur"(?:'(?:(?:\\\\)?(?:\\')?[^']?)*')?")
head, tail = ur'(?:\r?\n[ \t]*)?', ur'(?:[ \t\r]*\n)?'
inc = ur'((?:(?:path\s*=\s*)?%s\s+)?(?:expr\s*=\s*)?%s)' % (string, string)
template_search = re.compile(u'|'.join([
ur'(\$\$|%%)',
ur'\${((?:%s[^}]?)*)}' % (string),
ur'%s%%(.*)(?:\n|$)%s' % (head, tail),
ur'%s<%%def\s+(?:name\s*=\s*)?(%s)\s*>%s' % (head, string, tail),
ur'%s<\s*/\s*%%(def)>%s' % (head, tail),
ur'%s<%%inc(?:lude)?\s+%s\s*>%s' % (head, inc, tail),
ur'%s(<%%)' % head
]), re.M|re.I|re.U).search
template_include = re.compile((
ur'(?:(?:path\s*=\s*)?(%s)\s+)?'
ur'(?:expr\s*=\s*)?(%s)') % (string, string)).match
template_incargs = re.compile(ur'\s*([^\d]\w*)\s*\((.*)\)\s*').match
template_tags = re.compile((
ur'^[ \t]*(?:(if[ \t])|(else)|(elif[ \t])|(for[ \t])|'
ur'(endif)|(endfor))'), re.I|re.U).match
template_indent = re.compile(ur'^([ \t]*)(.*)', re.I|re.U).match
template_1starg = re.compile(ur'^\s*(\w+)\s*\(\s*(\w+)').match
template_closcr = re.compile((
ur'(?:"(?:(?:\\\\)?(?:\\")?[^"]?)*")|'
ur"(?:'(?:(?:\\\\)?(?:\\')?[^']?)*')|"
ur'(?:(%%>)%s)' % tail)).search
normpath = re.compile(r'//+').sub
pathmatch = re.compile(r'((?:/[^/]+)*)/?').match
not_forbidden = re.compile(r'^((?:/(?:[^.][^/]*)?)*/)([^.][^/]*)?$').match
def config_sessid_string(sessid_str):
    globals()['is_connid'] = re.compile((
r'^(?:(?:id=([0-9a-zA-Z]{16,128}))|(?:new=([0-9a-zA-Z]{16,128})))'
r'(?:&%s=([-_0-9a-zA-Z]{0,128}))?$') % sessid_str).match
    globals()['sessid_to_set_cookie'] = '''\
Set-Cookie: %s=%%s; path=/; expires=%%s''' % sessid_str
    globals()['sessid_from_cookie'] = re.compile((r'(?:^|;)\s*%s\s*=\s*'
r'([0-9a-zA-Z]{16,128})\s*(?:$|;)') % sessid_str).match
config_sessid_string('_SESS_')
get_protocol_connid = re.compile(r'\s*(\w{2})([0-9a-zA-Z]{1,128})\s*').match
is_base10 = re.compile(r'^[0-9]+$').match
is_base36 = re.compile(r'^[0-9a-zA-Z]{0,8}$').match
is_header  = re.compile(
r'^[\s\t]*([^\r\n:]+)[\s\t]*:[\s\t]*([^\r\n]+)[\s\t]*\r?\n$').match
rsa_text_noout = re.compile((
r'^private-key:(.*)\nmodulus:(.*)\npublicexponent:(.*)\nprivateexponent:(.*'
r')\nprime1:(.*)\nprime2:(.*)\nexponent1:(.*)\nexponent2:(.*)\ncoefficient:'
r'(.*)$'), re.I|re.S).match

str_EINVAL = 'Invalid argument'
str_ENOENT = 'No such file or directory'
jsonp_drop_s = '_mapfs_._%s_.drop();\n'
jsonp_keep_s = '_mapfs_._%s_.SLOT_keep();\n'
jsonp_done_s = '_mapfs_._%s_.SLOT_done("00", %s);\n'
jsonp_exec_s = '_mapfs_._%s_.SLOT_exec("00", %s);\n'
jsonp_handshake_s = '_mapfs_._%s_.SLOT_handshake();\n'
jsonp_submit_s = '_mapfs_._%s_.SLOT_submit("00", %s);\n'
jsonp_secure_done_s = '_mapfs_._%s_.SLOT_done("01", "%s");\n'
jsonp_secure_exec_s = '_mapfs_._%s_.SLOT_exec("01", "%s");\n'
jsonp_secure_submit_s = '_mapfs_._%s_.SLOT_submit("01", "%s");\n'
jsonp_init_s = '_mapfs_._%s_.SLOT_init("%s", "%s", null);\n'
jsonp_init_sessid_s = '_mapfs_._%s_.SLOT_init("%s", "%s", "%s");\n'
jsonp_headers = [('Content-Type', 'text/javascript'),
    ('Pragma', 'no-cache'), ('Cache-Control', 'no-cache, must-revalidate'),
    ('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT')]
jsonp_h = '''\
HTTP/1.1 200 OK\r\nContent-Type: text/javascript\r\nContent-Length: %d\r\nP\
ragma: no-cache\r\nCache-Control: no-cache, must-revalidate\r\nExpires: Mon\
, 26 Jul 1997 05:00:00 GMT\r\n\r\n%s'''
accepted_h = '''HTTP/1.1 202 Accepted\r\nContent-Type: text/plain\r\nConten\
t-Length: 12\r\n\r\n202 Accepted'''

mapfs_hooks = { '__api__.js': mapfsapijs, '__rsa__.js': mapfsrsajs,
    '__jsonp__.js': mapfsjsonp, '__post__.html': mapfspost }
mapfs_reserved = {'.py': 0, '.pyc': 0, '.psp': 0}
module_builtins = {'STR': STR}
file_headers = '''Content-Type: %s\r\nContent-Length: %d\r\nEtag: %s\r\nLas\
t-Modified: %s\r\n\r\n'''
rawheaders = '''Content-Type: %s\r\nContent-Length: %d\r\nEtag: %s\r\nLast-\
Modified: %s\r\n\r\n'''
zheaders = '''Content-Type: %s\r\nContent-Length: %d\r\nContent-Encoding: d\
eflate\r\nEtag: %s\r\nLast-Modified: %s\r\n\r\n'''
INDENT, WRITELINES = u'    ', u'''writelines(%s)'''
FUNCHEAD = u'''CTX, writelines = %s, %s.writelines; CTX.flush()'''
FUNCHEADMAIN = u'''CTX, writelines = %s, %s.writelines
CTX.start_response('200 OK', 'Content-Type: %s')'''
FUNCTAIL = u''''''
FUNCTAILMAIN = u'''CTX.close()'''
MODULEHEAD =u'''\
#
# DO NOT EDIT THIS FILE
# generated by liubao-%s
#
''' % __version__
NEWCACHE, ISFILE, ISDIR, NOTMODIFIED, FORBIDDEN, NOTFOUND = range(6)
TEXT, EXPR, IF, ELSE, ELIF, FOR, ENDIF, ENDFOR, DEF, ENDDEF, INCLUDE, PY = \
range(12)

import sqlite3
import sys, imp
import zlib, base64
from json import dumps
from uuid import uuid4
from weakref import ref
from hashlib import md5
from _blist import blist
from zlib import compress
from shutil import rmtree
from _socket import error
from errno import ETIMEDOUT
from cgi import parse_header
from ctypes import get_errno
from binascii import b2a_hex
from collections import deque
from cStringIO import StringIO
from urllib import unquote_plus
from traceback import print_exc
from fcntl import fcntl, F_SETFL
from subprocess import Popen, PIPE
from time import gmtime, strftime, time
from stat import S_ISDIR as stat_S_ISDIR
from greenlet import getcurrent, greenlet
from binascii import a2b_base64, b2a_base64
from bisect import bisect_left, bisect_right
from marshal import dumps as marshal_dumps, loads
from eurasia import apply_, meminfo, Timeout, \
    idle_switch, timer_switch, timer_switch_raise
from posixpath import basename, dirname, exists, isdir, isfile, \
    join, sep, splitext
from os import mkdir, strerror, unlink, urandom, O_NONBLOCK, O_RDONLY, \
    open as os_open, close as os_close, stat as os_stat

raw = zlib.decompress(base64.decodestring(r'''\
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\
'''))
z, now = compress(raw, 9)[2:-4], time()
apijs_last_modified = strftime('%a, %d-%b-%Y %H:%M:%S GMT', gmtime(now))
apijs_etag = md5(apijs_last_modified).hexdigest()
apijs_z_h = '''HTTP/1.1 200 OK\r\nEtag: %s\r\nLast-Modified: %s\r\nCont\
ent-Type: text/javascript\r\nContent-Length: %d\r\nContent-Encoding: deflat\
e\r\n\r\n%s''' % (apijs_etag, apijs_last_modified, len(z), z)
apijs_h = '''HTTP/1.1 200 OK\r\nEtag: %s\r\nLast-Modified:%s\r\nContent\
-Type: text/javascript\r\nContent-Length: %d\r\n\r\n%s''' % (
    apijs_etag, apijs_last_modified, len(raw), raw)

raw = '''\
// BigInt, a suite of routines for performing multiple-precision arithmetic
// in JavaScript.
//
// Copyright 1998-2005 David Shapiro.
//
// You may use, re-use, abuse,
// copy, and modify this code to your liking, but please keep this header.
// Thanks!
//
// Dave Shapiro
// dave@ohdave.com
''' + zlib.decompress(base64.decodestring(r'''\
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\
n'''))
z = compress(raw, 9)[2:-4]
rsajs_z_h = '''HTTP/1.1 200 OK\r\nEtag: %s\r\nLast-Modified: %s\r\nCont\
ent-Type: text/javascript\r\nContent-Length: %d\r\nContent-Encoding: deflat\
e\r\n\r\n%s''' % (apijs_etag, apijs_last_modified, len(z), z)
rsajs_h = '''HTTP/1.1 200 OK\r\nEtag: %s\r\nLast-Modified:%s\r\nContent\
-Type: text/javascript\r\nContent-Length: %d\r\n\r\n%s''' % (
    apijs_etag, apijs_last_modified, len(raw), raw)

exec ('''del h1, head, i, inc, lines, name, p, raw, re, tail, title, z''')

############################################################################
